package com.yyx.qq.helpers;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.yyx.qq.QQMain;
import com.yyx.qq.MessageQueue;
import com.yyx.qq.models.GroupType;
import com.yyx.qq.models.ModelRegistry;
import com.yyx.qq.models.User;

import edu.tsinghua.lumaqq.qq.QQ;
import edu.tsinghua.lumaqq.qq.Util;
import edu.tsinghua.lumaqq.qq.beans.ClusterIM;
import edu.tsinghua.lumaqq.qq.beans.NormalIM;
import edu.tsinghua.lumaqq.qq.packets.in.ReceiveIMPacket;

/**
 * 消息处理帮助类
 * 
 * @author luma
 */
public class MessageHelper {
	private QQMain main;
	// 分片缓冲，有的长消息会变成几个分片发送，需要保存起来等待所有分片完成
	// key是消息id，value是个Object数组，保存了消息的分片
	private Map<Integer, Object[]> fragmentCache;

	public MessageHelper(QQMain main) {
		this.main = main;
		fragmentCache = new HashMap<Integer, Object[]>();
	}

	/**
	 * 把字节数组转换为String，它为我们处理缺省表情的问题
	 * 
	 * @param b
	 *            消息字节数组
	 * @return String
	 */
	public String convertBytes(byte[] b) {
		StringBuffer sb = new StringBuffer();
		int offset = 0;
		int length = 0;
		for (int i = 0; i < b.length; i++) {
			if (b[i] == QQ.QQ_TAG_DEFAULT_FACE) {
				sb.append(Util.getString(b, offset, length));
				sb.append((char) b[i]).append((char) (b[i + 1] & 0xFF));
				i++;
				offset = i + 1;
				length = 0;
			} else
				length++;
		}
		if (length > 0)
			sb.append(Util.getString(b, offset, length));
		return sb.toString();
	}

	/**
	 * 检查这个消息是完整消息还是分片
	 * 
	 * @return true表示这个消息是分片消息
	 */
	private boolean isFragment(NormalIM im) {
		return im.totalFragments > 1;
	}

	/**
	 * 检查这个消息是完整消息还是分片
	 * 
	 * @return true表示这个消息是分片消息
	 */
	private boolean isFragment(ClusterIM im) {
		return im.fragmentCount > 1;
	}

	/**
	 * 添加一个普通消息分片
	 * 
	 * @param im
	 */
	private void addFragment(NormalIM im) {
		Object[] fragments = fragmentCache.get(im.messageId);
		if (fragments == null || fragments.length != im.totalFragments) {
			fragments = new Object[im.totalFragments];
			fragmentCache.put(im.messageId, fragments);
		}
		fragments[im.fragmentSequence] = im;
	}

	/**
	 * 添加一个群消息分片
	 * 
	 * @param im
	 */
	private void addFragment(ClusterIM im) {
		Object[] fragments = fragmentCache.get(im.messageId);
		if (fragments == null || fragments.length != im.fragmentCount) {
			fragments = new Object[im.fragmentCount];
			fragmentCache.put(im.messageId, fragments);
		}
		fragments[im.fragmentSequence] = im;
	}

	/**
	 * 得到完整的消息，同时把这个消息从分片缓冲中清楚。调用此方法前，必须先用 isMessageComplete()判断分片是否都已经收到
	 * 
	 * @param messageId
	 *            消息id
	 * @return ClusterIM对象
	 */
	private ClusterIM getIntegratedClusterIM(int messageId) {
		Object[] fragments = fragmentCache.remove(messageId);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		for (Object f : fragments) {
			try {
				baos.write(((ClusterIM) f).messageBytes);
			} catch (IOException e) {
			}
		}
		ClusterIM ret = (ClusterIM) fragments[fragments.length - 1];
		ret.messageBytes = baos.toByteArray();
		ret.message = convertBytes(ret.messageBytes);
		return ret;
	}

	/**
	 * 得到完整的普通消息
	 * 
	 * @param messageId
	 *            消息ID
	 * @return NormalIM对象
	 */
	private NormalIM getIntegratedNormalIM(int messageId) {
		Object[] fragments = fragmentCache.remove(messageId);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		for (Object f : fragments) {
			try {
				baos.write(((NormalIM) f).messageBytes);
			} catch (IOException e) {
			}
		}
		NormalIM ret = (NormalIM) fragments[0];
		ret.message = NormalIMFormalizer.deformalize(baos.toByteArray());
		return ret;
	}

	/**
	 * 检查是否一个长消息的分片都已经收到了
	 * 
	 * @param messageId
	 *            消息id
	 * @return true表示已经收到
	 */
	private boolean isMessageComplete(int messageId) {
		if (!fragmentCache.containsKey(messageId))
			return false;

		Object[] fragments = fragmentCache.get(messageId);
		for (Object f : fragments) {
			if (f == null)
				return false;
		}
		return true;
	}

	/**
	 * 推入一条消息并更新各种图标的闪烁状态，
	 * 这个方法会检查是否好友列表已经 得到，如果没有，推入延迟队列
	 * 
	 * @param packet
	 *            消息包
	 */
	public void putNormalIM(ReceiveIMPacket packet) {
		// 如果现在是延迟处理
		MessageQueue mq = main.getMq();
		if (mq.isPostpone()) {
			mq.postponeMessage(packet);
			return;
		}

		// 如果这个消息是分片消息，如果这个消息已经完成，则继续处理，否则推入分片缓冲
		if (isFragment(packet.normalIM)) {
			addFragment(packet.normalIM);
			if (isMessageComplete(packet.normalIM.messageId)) {
				packet.normalIM = getIntegratedNormalIM(packet.normalIM.messageId);
			} else {
				return;
			}
		} else {
			packet.normalIM.message = NormalIMFormalizer
					.deformalize(packet.normalIM.messageBytes);
		}

		// 得到好友在model中的位置，但是有可能为null，因为也许这是用户的第一次登陆
		// 其好友列表还没得到，但是这时候有消息来了，所有也无法闪烁图标了，对于
		// 这种情况，需要特殊处理一下，基本的方法是把消息推入延迟处理队列
		User f = ModelRegistry.getUser(packet.normalHeader.sender);
		boolean iAmHisStranger = packet.header.type == QQ.QQ_RECV_IM_STRANGER;
		boolean noSuchUser = f == null || f.group.isCluster();
		// boolean heIsMyStranger = noSuchUser || f.group.isStranger();
		boolean heIsMyBlacklist = f != null && f.group.isBlackList();
		if (heIsMyBlacklist || noSuchUser && iAmHisStranger) {
			return;
		}
		// if (heIsMyStranger) return;
		UIHelper uihelper = main.getUiHelper();
		// 如果不存在这个用户，添加到陌生人
		if (noSuchUser) {
			f = new User();
			f.qq = packet.normalHeader.sender;
			f.nick = String.valueOf(f.qq);
			f.displayName = f.nick;
			uihelper.addUser(f, GroupType.STRANGER_GROUP);
			main.getUiHelper().refreshGroup();
			main.getClient().user_GetInfo(f.qq);
		}
		uihelper.appendMessage(f, packet.normalIM, packet.normalHeader);
		// 推入队列
		mq.putMessage(packet);
	}
}
